﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace Sustainalytics.Caching.Tests
{
    public abstract class AppCacheTests
    {
        protected IAppCache AppCache { get; set; }

        protected abstract IAppCache CreateAppCache();

        protected virtual void ClearAppCache()
        {
            AppCache.Clear();
        }

        [TestInitialize]
        public virtual void TestInitialize()
        {
            AppCache = CreateAppCache();
            ClearAppCache();
        }

        [TestCleanup]
        public virtual void TestCleanup()
        {
            ClearAppCache();
        }

        [TestMethod]
        [ExpectedException(typeof(CacheItemKeyNullOrEmptyException))]
        public virtual void Set_StringValue_KeyNull_Exception()
        {
            AppCache.Set(null, "123");
        }

        [TestMethod]
        public virtual void Set_StringValue_ValueNull_Success()
        {
            AppCache.Set("1", (string)null);
        }

        [TestMethod]
        public virtual void Set_StringValue_Success()
        {
            AppCache.Set("1", "123");
        }

        [TestMethod]
        [ExpectedException(typeof(CacheItemKeyNullOrEmptyException))]
        public virtual void Set_IEnumerableValue_KeyNull_Exception()
        {
            AppCache.Set(null, new string[] { "123", "456" });
        }

        [TestMethod]
        public virtual void Set_IEnumerableValue_ValueNull_Success()
        {
            AppCache.Set("1", (IEnumerable<string>)null);
        }

        [TestMethod]
        public virtual void Set_IEnumerableValue_Success()
        {
            AppCache.Set("1", new string[] { "123", "456" });
        }

        [TestMethod]
        [ExpectedException(typeof(CacheItemKeyNullOrEmptyException))]
        public virtual void GetString_KeyNull_Exception()
        {
            AppCache.GetString(null);
        }

        [TestMethod]
        public virtual void GetString_Success()
        {
            string key = "1";
            string expectedValue = "123";

            AppCache.Set(key, expectedValue);

            string value = AppCache.GetString(key);

            Assert.IsTrue(expectedValue == value, "Expected value is not correct.");
        }

        [TestMethod]
        [ExpectedException(typeof(CacheItemKeyNullOrEmptyException))]
        public virtual void GetSet_KeyNull_Exception()
        {
            AppCache.GetSet(null);
        }

        [TestMethod]
        public virtual void GetSet_Success()
        {
            string key = "1";
            IEnumerable<string> expectedValue = new string[] { "123", "456" };

            AppCache.Set(key, expectedValue);

            IEnumerable<string> value = AppCache.GetSet(key);

            Assert.IsNotNull(value, "Returned value is null.");
            Assert.IsTrue(expectedValue.SequenceEqual(value), "Returned set is different than the one expected.");
        }

        [TestMethod]
        [ExpectedException(typeof(CacheItemKeyNullOrEmptyException))]
        public virtual void Remove_KeyNull_Exception()
        {
            AppCache.Remove(null);
        }

        [TestMethod]
        public virtual void Remove_Success()
        {
            string key = "1";
            string value = "123";

            AppCache.Set(key, value);

            AppCache.Remove(key);

            string retrievedValue = AppCache.GetString(key);

            Assert.IsNull(retrievedValue, "Key was not deleted.");
        }

        [TestMethod]
        public virtual void Subscribe_Set()
        {
            string key = "1";
            string value = "123";

            bool ok = false;

            AppCache.Subscribe(cacheItemAction =>
                {
                    Assert.IsTrue(cacheItemAction.CacheItem.Key == key, "Key is different.");
                    Assert.IsTrue((string)cacheItemAction.CacheItem.Value == value, "Value is different.");
                    Assert.IsTrue(cacheItemAction.ActionType == AppCacheItemActionType.Set, "Action type is incorrect.");

                    ok = true;
                });

            AppCache.Set(key, value);

            if (!ok)
            {
                Thread.Sleep(500);
            }

            Assert.IsTrue(ok, "No subscription");
        }

        [TestMethod]
        public virtual void Subscribe_Remove()
        {
            string key = "1";
            string value = "123";

            AppCache.Set(key, value);

            bool ok = false;

            AppCache.Subscribe(cacheItemAction =>
            {
                Assert.IsTrue(cacheItemAction.CacheItem.Key == key, "Key is different.");
                Assert.IsTrue((string)cacheItemAction.CacheItem.Value == value, "Value is different.");
                Assert.IsTrue(cacheItemAction.ActionType == AppCacheItemActionType.Removed, "Action type is incorrect.");

                ok = true;
            });

            AppCache.Remove(key);

            if (!ok)
            {
                Thread.Sleep(500);
            }

            Assert.IsTrue(ok, "No subscription");
        }
    }
}
